#!/usr/bin/env python
#-*- coding: UTF-8 -*-

# Stupid (buggy) HTTP (Get) Cache
# ranges header saadetakse tagasi aga cache ise ei oska veel range'ga midagi teha
# ja ?&%¤# ei escape ära ka veel, et siis search?q=a&b=c viskab mmingit errorit
#
# works like :
#   wget http://localhost:6666/www.google.com/seach
import httplib, urllib
import os
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import re
from ansi import *

CACHE_PREFIX='./cache/'

re_host = re.compile(r'/(?P<host>.*?)/', re.I)
re_query = re.compile(r'/.*?/(?P<query>.*)', re.I)

#print re_host.search('/www.ee').groups() # <---ERROR :(
#print re_host.search('/www.ee/').groups()
#print re_file.search('/www.ee/index.html').groups()
#print re_host.search('/www.ee/index.html').groups()


def pretty(size):
    t = ['B','KB']

def console_size():
    return os.popen('stty size', 'r').read().split()

# expired time check maybe :P hah
# -1 to force getting from cache even if server doesn't send us content-length
def check_file(path, remote_size):
    if os.path.exists(path) and (os.stat(path).st_size == remote_size): ## or remote_size == -1): #fucks up ranges
        return True
    return False

def file_get(file, offset, length, sock):
    block = 8192
    f = open(file,'rb')
    while length > 0:
        if length < 8192: block = length
        data = f.read(block)
        length -= block
        if not data: break
        sock.wfile.write(data)
        sock.wfile.flush()
    f.close()


def http_get(host, query, file, sock):
    partial = {}
    h = {}

    conn = httplib.HTTPConnection(host)
    for i in sock.headers:
        h[i] = sock.headers.getheader(i)
    h['host'] = host

    conn.request("GET", "/" + query, headers = h)
    response = conn.getresponse()
    sock.send_response(response.status)
    #print "HTTP GET: http://%s/%s" % (host, query),response.status, response.reason

    #content-range : bytes 440-29984/190577
    
    for k,v in response.getheaders():
        if k == 'content-range':
            m = re.search(r'(?P<unit>\w*?)\s*(?P<start>\d+)-(?P<end>\d+)/?(?P<size>\d*)',v, re.I)
            if m:
                partial['unit'] = m.group('unit')
                partial['start'] = m.group('start')
                partial['end'] = m.group('end')
                partial['size'] = m.group('size') #Whole file/instance size, not end-start

    if response.status == 200 or response.status == 206 :
        try:
            if not os.path.exists(os.path.dirname(CACHE_PREFIX + file)):
                os.makedirs(os.path.dirname(CACHE_PREFIX + file))

            fcache = open(CACHE_PREFIX + file, 'wb')

            if partial.has_key('start') and partial['start'] != '':
                fcache.seek(int(partial['start']))
            elif partial.has_key('end') and partial['end'] != '':
                fcache.seek( -int(partial['start']), 2) # This should fill file with '\0' up to the seek point 
                                                        # if there is no data already in the file

            for k,v in response.getheaders():
                sock.send_header(k,v)
            sock.end_headers()
            while True:    
                data = response.read(8192)
                if not data: break
                fcache.write(data)
                sock.wfile.write(data)
                sock.wfile.flush()
            fcache.close()
        except IOError:
            sock.send_error(404, "IOError: %s" % file)
    conn.close()

def http_head(host, file, headers):
    '''
        Headers class is  mimetools.Message
    '''
    conn = httplib.HTTPConnection(host)

    h = {}
    for i in headers:
        h[i] = headers.getheader(i)

    conn.request("HEAD", "/"+file, headers=h)
    response = conn.getresponse()
    h = response.getheaders()
    conn.close()
    return h


class CacheHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        remote_size = -1
        partial = {}
        #try:
        if True:
            #print self.path
            host_match = re_host.search(self.path)
            query_match = re_query.search(self.path)

            if not host_match:
                self.send_error(404,'Host match error with: %s'%self.path)
                return

            host = host_match.group('host')
            if query_match:
                query = query_match.group('query')
            else:
                query = ""
            file = host + "/" + query

            headers = http_head(host, query, self.headers)
            for k,v in headers:
                #print k,':',v
                if k == "content-length": 
                    remote_size = int(v)

            for k in self.headers:
                v = self.headers.getheader(k)
                if k == 'content-range':
                    m = re.search(r'(?P<unit>\w*?)\s*(?P<start>\d+)-(?P<end>\d+)/?(?P<size>\d*)',v, re.I)
                    if m:
                        partial['unit'] = m.group('unit')
                        partial['start'] = m.group('start')
                        partial['end'] = m.group('end')
                        partial['size'] = m.group('size') #Whole file/instance size, not end-start

            #print "Content length:", remote_size


            #for f in [file, file + "index.html"]: # ignore dir. listings
            # Veits parem vist, proov teha nii, et centos installer partial contenti uuesti ka ei tiriks kui jupp
            # juba olemas on, dunno
            if os.path.exists(CACHE_PREFIX + file) and os.path.isfile(CACHE_PREFIX + file):
                filesize = os.stat(CACHE_PREFIX + file).st_size
                if partial.has_key('start') and partial.has_key('end'):
                    length = filesize

                    if partial['start'] != '' and partial['end'] != '':  
                        length = int(partial['end']) - int(partial['start'])

                    if partial['start'] == '' and partial['end'] != '':  
                        length = int(partial['end'])
                        start = filesize - int(partial['end']) 

                    if start < filesize and start + length <= filesize:
                        self.send_response(206)
                        self.send_header('content-length', length)
                        self.send_header('content-range', 'bytes %d-%d/%d' %( partial['start'], partial['end'], partial['size']) )
                        self.end_headers()
                        file_get(CACHE_PREFIX + file, start, length, self)
                        return
                elif filesize == remote_size:
                    self.send_response(200)
                    self.send_header('content-length', str(os.stat(CACHE_PREFIX + file).st_size))
                    self.end_headers()
                    file_get(CACHE_PREFIX + file, 0, os.stat(CACHE_PREFIX + file).st_size, self)
                    return

            if file.endswith("/"): file += "index.html"

            http_get(host, query, file, self)

            #####
            #self.send_response(200)
            #self.send_header('Content-type', 'text/plain')
            #self.wfile.write(self.path)
        #except Exception as e:
        #    self.send_error(404,'Error with: %s'% e)

def main():
    try:
        server = HTTPServer(('',6666), CacheHandler)
        print 'start'
        server.serve_forever()
    except KeyboardInterrupt:
        print 'killing...'
        server.socket.close()

if __name__=='__main__':
    main()

#for k,v in http_head('www.python.org','/doc/2.5.2/lib/httpresponse-objects.html'):
#    print k,'=',v
